package me.stone.stanimeclient.core.ui.adapters.base

import android.view.View
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import me.stone.stanimeclient.core.ui.adapters.listener.OnItemSelectListener
import me.stone.stanimeclient.core.ui.extensions.getPositionTag
import me.stone.stanimeclient.core.ui.extensions.setPositionTag
import java.lang.ref.SoftReference
import java.util.concurrent.Executor

abstract class AbsSelectStateAdapter<T: Any, V: ViewHolder>(
    backExecutor: Executor,
    private val diffCallback: DiffUtil.ItemCallback<T>
): ListAdapter<T, V>(
    AsyncDifferConfig.Builder(diffCallback)
        .setBackgroundThreadExecutor(backExecutor)
        .build()
) {

    var itemSelectListener: OnItemSelectListener<T>? = null

    var enableDefaultSelected: Boolean = true

    protected var mSelectedItemPair: Pair<Int, T>? = null

    open val mSelectItemPos: Int?
        get() = mSelectedItemPair?.first

    open val mSelectItemVal: T?
        get() = mSelectedItemPair?.second

    private val mClickListener = View.OnClickListener { view ->
        val position = view.getPositionTag()

        if (mSelectItemVal == getItem(position)
            || (mSelectItemVal == null && position == 0 && enableDefaultSelected)) {
            // 点击的项目和已选的项目一致则直接退出流程
            return@OnClickListener
        }

        if (!itemCanSelect(position)) {
            // 如果点击的项目不可选择直接退出流程
            return@OnClickListener
        }

        // 取消上一次选择的项目，如果没有通知0号项目更新
        mSelectItemVal?.let {
            onItemUnselect(it)
        }
        // 第一个项目默认选中状态
        val oldPos = mSelectItemPos ?: 0

        // 设置选择的项目，通知项目View更新显示内容
        val item = getItem(position)
        mSelectedItemPair = position to item
        onItemSelected(item)

        notifyItemChanged(oldPos)
        notifyItemChanged(position)
    }

    protected var mRecyclerView: SoftReference<RecyclerView>? = null

    abstract fun bindViewData(holder: V, position: Int, item: T)

    override fun onBindViewHolder(holder: V, position: Int) {
        holder.itemView.run {
            setPositionTag(position)
            setOnClickListener(mClickListener)
        }

        val item = getItem(position)
        bindViewData(holder, position, item)

        if (!itemCanSelect(position)) { return }
        if (mSelectItemVal == item
            || (mSelectItemVal == null && position == 0 && enableDefaultSelected)) {
            onItemViewSelected(holder.itemView)
        } else {
            onItemViewUnSelect(holder.itemView)
        }
    }

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
        mRecyclerView = SoftReference(recyclerView)
    }

    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
        mRecyclerView?.clear()
        mRecyclerView = null
    }

    open fun itemCanSelect(position: Int): Boolean {
        return true
    }

    open fun onItemSelected(item: T) {
        itemSelectListener?.onItemSelected(item)
    }

    open fun onItemUnselect(item: T) {
        itemSelectListener?.onItemUnselect(item)
    }

    open fun onItemViewSelected(view: View) {

    }

    open fun onItemViewUnSelect(view: View) {

    }

    open fun indexOfItem(item: T): Int {
        return currentList.indexOf(item)
    }

    fun updateSelectedItem(item: T) {
        if (item == mSelectItemVal) { return }
        val position = currentList.indexOf(item)
        mSelectItemPos?.let { notifyItemChanged(it) }
        mSelectedItemPair = position to item
        if (position == -1) { return }
        notifyItemChanged(position)
        // 移动到已选项目
        mRecyclerView?.get()?.scrollToPosition(position)
    }

    override fun submitList(list: List<T>?) {
        super.submitList(list) {
            list?.let { updateSelected(it) }
        }
    }

    override fun submitList(list: List<T>?, commitCallback: Runnable?) {
        super.submitList(list) {
            list?.let { updateSelected(it) }
            commitCallback?.run()
        }
    }

    open fun updateSelected(list: List<T>) {
        val selected = mSelectedItemPair ?: return
        val newPos = list.indexOfFirst {
            diffCallback.areItemsTheSame(selected.second, it) &&
                    diffCallback.areContentsTheSame(selected.second, it)
        }
        mSelectedItemPair = if (newPos != -1) { newPos to list[newPos] } else null
    }


}